
# Copyright 2010 István Szentandrási
# <etude-music-player@googlegroups.com>

# This file is part of Etude.
#
# Etude is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as 
# published by the Free Software Foundation, either version 3 
# of the License, or (at your option) any later version.
#
# Etude is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Etude. If not, see <http://www.gnu.org/licenses/>.

"""
Chopped down pythonbindings for visualization support for XMMS2.
"""

cdef extern from "Python.h":
	object PyUnicode_DecodeUTF8(char *unicode, int size, char *errors)
	object PyUnicode_AsUTF8String(object o)
	object PyString_FromStringAndSize(char *, int)
	void Py_INCREF(object)
	void Py_DECREF(object)
	void PyMem_Free(void *p)
	void* PyMem_Malloc(int n) except NULL

cdef extern from "xmms_pyrex_hacks.h":
	# pyrex doesn't know about const, so we do some tricks to get
	# rid of compiler warnings
	ctypedef struct xmms_pyrex_constcharp_t:
		pass
	ctypedef struct xmms_pyrex_constcharpp_t:
		pass
	ctypedef struct xmms_pyrex_constucharpp_t:
		pass


cdef extern from "xmmsc/xmmsv.h":
	ctypedef enum xmmsv_type_t:
		XMMSV_TYPE_NONE,
		XMMSV_TYPE_ERROR,
		XMMSV_TYPE_INT32,
		XMMSV_TYPE_STRING,
		XMMSV_TYPE_COLL,
		XMMSV_TYPE_BIN,
		XMMSV_TYPE_LIST,
		XMMSV_TYPE_DICT,

	ctypedef struct xmmsv_t
	ctypedef struct xmmsv_coll_t

	xmmsv_t *xmmsv_new_none   ()
	xmmsv_t *xmmsv_new_error  (char *errstr)
	xmmsv_t *xmmsv_new_int    (int i)
	xmmsv_t *xmmsv_new_uint   (unsigned int u)
	xmmsv_t *xmmsv_new_string (char *s)
	xmmsv_t *xmmsv_new_coll   (xmmsv_coll_t *coll)
	xmmsv_t *xmmsv_new_bin    (unsigned char *data, unsigned int len)

	xmmsv_t *xmmsv_new_list ()
	xmmsv_t *xmmsv_new_dict ()

	xmmsv_t *xmmsv_ref (xmmsv_t *val)
	void xmmsv_unref   (xmmsv_t *value)

	xmmsv_type_t xmmsv_get_type(xmmsv_t *res)

	int  xmmsv_is_error (xmmsv_t *res)
	int  xmmsv_is_list  (xmmsv_t *res)
	int  xmmsv_is_dict  (xmmsv_t *val)

	int  xmmsv_get_error      (xmmsv_t *value, xmms_pyrex_constcharpp_t r)
	int  xmmsv_get_int        (xmmsv_t *res, int *r)
	int  xmmsv_get_string     (xmmsv_t *res, xmms_pyrex_constcharpp_t r)
	int  xmmsv_get_bin        (xmmsv_t *res, xmms_pyrex_constucharpp_t r, unsigned int *rlen)

	ctypedef void (*xmmsv_list_foreach_func) (xmmsv_t *value, void *user_data)

	int  xmmsv_list_get (xmmsv_t *listv, int pos, xmmsv_t **val)
	int  xmmsv_list_append (xmmsv_t *listv, xmmsv_t *val)
	int  xmmsv_list_insert (xmmsv_t *listv, int pos, xmmsv_t *val)
	int  xmmsv_list_remove (xmmsv_t *listv, int pos)
	int  xmmsv_list_clear (xmmsv_t *listv)
	int  xmmsv_list_foreach (xmmsv_t *listv, xmmsv_list_foreach_func func, void* user_data)
	int  xmmsv_list_get_size (xmmsv_t *listv)

	ctypedef struct xmmsv_list_iter_t

	int  xmmsv_get_list_iter    (xmmsv_t *val, xmmsv_list_iter_t **it)

	int  xmmsv_list_iter_entry  (xmmsv_list_iter_t *it, xmmsv_t **val)
	int  xmmsv_list_iter_valid  (xmmsv_list_iter_t *it)
	void xmmsv_list_iter_first  (xmmsv_list_iter_t *it)
	void xmmsv_list_iter_next   (xmmsv_list_iter_t *it)
	int  xmmsv_list_iter_seek   (xmmsv_list_iter_t *it, int pos)

	int  xmmsv_list_iter_insert (xmmsv_list_iter_t *it, xmmsv_t *val)
	int  xmmsv_list_iter_remove (xmmsv_list_iter_t *it)

	void xmmsv_list_iter_explicit_destroy (xmmsv_list_iter_t *it)

	ctypedef void (*xmmsv_dict_foreach_func) (char *key, xmmsv_t *value, void *user_data)

	int  xmmsv_dict_get     (xmmsv_t *dictv, char *key, xmmsv_t **val)
	int  xmmsv_dict_set     (xmmsv_t *dictv, char *key, xmmsv_t *val)
	int  xmmsv_dict_remove  (xmmsv_t *dictv, char *key)
	int  xmmsv_dict_clear   (xmmsv_t *dictv)
	int  xmmsv_dict_foreach (xmmsv_t *dictv, xmmsv_dict_foreach_func func, void *user_data)

	ctypedef struct xmmsv_dict_iter_t

	int  xmmsv_get_dict_iter   (xmmsv_t *val, xmmsv_dict_iter_t **it)

	int  xmmsv_dict_iter_pair  (xmmsv_dict_iter_t *it, xmms_pyrex_constcharp_t *key, xmmsv_t **val)
	int  xmmsv_dict_iter_pair_string  (xmmsv_dict_iter_t *it, xmms_pyrex_constcharpp_t key, xmms_pyrex_constcharpp_t val)
	int  xmmsv_dict_iter_valid (xmmsv_dict_iter_t *it)
	void xmmsv_dict_iter_first (xmmsv_dict_iter_t *it)
	void xmmsv_dict_iter_next  (xmmsv_dict_iter_t *it)
	int  xmmsv_dict_iter_find  (xmmsv_dict_iter_t *it, char *key)

	int  xmmsv_dict_iter_set    (xmmsv_dict_iter_t *it, xmmsv_t *val)
	int  xmmsv_dict_iter_remove (xmmsv_dict_iter_t *it)

	void xmmsv_dict_iter_explicit_destroy (xmmsv_dict_iter_t *it)


VALUE_TYPE_NONE = XMMSV_TYPE_NONE
VALUE_TYPE_ERROR = XMMSV_TYPE_ERROR
VALUE_TYPE_INT32 = XMMSV_TYPE_INT32
VALUE_TYPE_STRING = XMMSV_TYPE_STRING
VALUE_TYPE_COLL = XMMSV_TYPE_COLL
VALUE_TYPE_BIN = XMMSV_TYPE_BIN
VALUE_TYPE_LIST = XMMSV_TYPE_LIST
VALUE_TYPE_DICT = XMMSV_TYPE_DICT

cdef extern from "xmmsclient/xmmsclient.h":
	ctypedef enum xmmsc_result_type_t:
		XMMSC_RESULT_CLASS_DEFAULT,
		XMMSC_RESULT_CLASS_SIGNAL,
		XMMSC_RESULT_CLASS_BROADCAST

	ctypedef struct xmmsc_connection_t:
		pass
	ctypedef struct xmmsc_result_t

	ctypedef int  (*xmmsc_result_notifier_t) (xmmsv_t *val, void *user_data)
	ctypedef void (*xmmsc_user_data_free_func_t) (void *user_data)
	ctypedef void (*xmmsc_io_need_out_callback_func_t) (int, void*)
	ctypedef void (*xmmsc_disconnect_func_t) (void *user_data)

	xmmsc_result_t *xmmsc_result_ref(xmmsc_result_t *res)
	void xmmsc_result_unref(xmmsc_result_t *res)
	void xmmsc_result_notifier_set (xmmsc_result_t *res, xmmsc_result_notifier_t func, void *user_data)
	void xmmsc_result_notifier_set_full(xmmsc_result_t *res, xmmsc_result_notifier_t func, void *user_data, xmmsc_user_data_free_func_t free_func)
	void xmmsc_result_wait(xmmsc_result_t *res)
	xmmsv_t *xmmsc_result_get_value(xmmsc_result_t *res)

	xmmsc_connection_t *xmmsc_init(char *clientname)

	void xmmsc_disconnect_callback_set(xmmsc_connection_t *c, xmmsc_disconnect_func_t callback, void *userdata)
	void xmmsc_disconnect_callback_set_full(xmmsc_connection_t *c, xmmsc_disconnect_func_t disconnect_func, void *userdata, xmmsc_user_data_free_func_t free_func)
	int xmmsc_connect(xmmsc_connection_t *c, char *p)
	void xmmsc_unref(xmmsc_connection_t *c)
	xmmsc_result_t *xmmsc_quit(xmmsc_connection_t *conn)
	xmmsc_result_t *xmmsc_main_list_plugins (xmmsc_connection_t *c, unsigned int type)

	void xmmsc_io_need_out_callback_set_full(xmmsc_connection_t *c, xmmsc_io_need_out_callback_func_t callback, void *userdata, xmmsc_user_data_free_func_t free_func)
	void xmmsc_io_disconnect(xmmsc_connection_t *c)
	int xmmsc_io_want_out(xmmsc_connection_t *c)
	int xmmsc_io_out_handle(xmmsc_connection_t *c)
	int xmmsc_io_in_handle(xmmsc_connection_t *c)
	int xmmsc_io_fd_get(xmmsc_connection_t *c)
	
	#visualization
	#/* commands */
	char* xmmsc_get_last_error(xmmsc_connection_t *c)
	xmmsc_result_t *xmmsc_visualization_version (xmmsc_connection_t *c)
	xmmsc_result_t *xmmsc_visualization_init (xmmsc_connection_t *c)
	#/* Returns 1 on success, 0 on failure. In that case, see xmmsc_get_last_error() */
	int xmmsc_visualization_init_handle (xmmsc_result_t *res)
	xmmsc_result_t *xmmsc_visualization_start (xmmsc_connection_t *c, int vv)
	void xmmsc_visualization_start_handle (xmmsc_connection_t *c, xmmsc_result_t *res)
	int xmmsc_visualization_started (xmmsc_connection_t *c, int vv)
	int xmmsc_visualization_errored (xmmsc_connection_t *c, int vv)

	xmmsc_result_t *xmmsc_visualization_property_set (xmmsc_connection_t *c, int v, char *key, char *value)
	xmmsc_result_t *xmmsc_visualization_properties_set (xmmsc_connection_t *c, int v, xmmsv_t *props)
	int xmmsc_visualization_chunk_get (xmmsc_connection_t *c, int vv, short *buffer, int drawtime, unsigned int blocking)
	void xmmsc_visualization_shutdown (xmmsc_connection_t *c, int v)

#####################################################################

from select import select
from os import write
import os
import traceback
import sys

cdef to_unicode(char *s):
	try:
		ns = PyUnicode_DecodeUTF8(s, len(s), NULL)
	except:
		ns = s
	return ns

cdef from_unicode(object o):
	if isinstance(o, unicode):
		return PyUnicode_AsUTF8String(o)
	else:
		return o

cdef int ResultNotifier(xmmsv_t *res, void *o):
	cdef object obj
	obj = <object> o
	if obj._callback():
		return 1
	return 0

cdef void ObjectFreeer(void *o):
	cdef object obj
	obj = <object> o
	Py_DECREF(obj)

from xmmsclient.propdict import PropDict


cdef class XMMSValue

cdef xmmsv_t *create_native_value(value):
	cdef xmmsv_t *ret

	if value is None:
		ret = xmmsv_new_none()
	elif isinstance(value, int):
		ret = xmmsv_new_int(value)
	elif isinstance(value, basestring):
		tmp = from_unicode(value)
		ret = xmmsv_new_string(tmp)
	elif isinstance(value, list):
		ret = xmmsv_new_list()
		for item in value:
			xmmsv_list_append(ret, create_native_value(item))
	elif isinstance(value, dict):
		ret = xmmsv_new_dict()
		for key,item in value.iteritems():
			tmp = from_unicode(key)
			xmmsv_dict_set(ret, tmp, create_native_value(item))
	return ret

cdef class XMMSValue:
	cdef object sourcepref
	cdef xmmsv_t *val
	cdef int ispropdict

	def __init__(self, sourcepref=None):
		self.sourcepref = sourcepref
		self.ispropdict = 0

	cdef set_value(self, xmmsv_t *value):
		xmmsv_ref(value)
		self.val = value

	def get_type(self):
		"""
		Return the type of data contained in this result.
		The return value is one of the OBJECT_CMD_ARG_* constants.
		"""
		return xmmsv_get_type(self.val)

	def value(self):
		"""
		Return value of appropriate data type contained in this result.
		This can be used instead of most get_* functions in this class.
		"""
		cdef xmmsv_type_t typ

		typ = self.get_type()

		if typ == XMMSV_TYPE_NONE:
			return None
		elif typ == XMMSV_TYPE_ERROR:
			return self.get_error()
		elif typ == XMMSV_TYPE_INT32:
			return self.get_int()
		elif typ == XMMSV_TYPE_STRING:
			return self.get_string()
		elif typ == XMMSV_TYPE_BIN:
			return self.get_bin()
		elif typ == XMMSV_TYPE_LIST:
			return self.get_list()
		elif typ == XMMSV_TYPE_DICT:
			if self.ispropdict:
				return self.get_propdict()
			return self.get_dict()
		else:
			raise TypeError("Unknown type returned from the server: %d." % typ)

	def get_int(self):
		"""
		Get data from the result structure as an int.
		@rtype: int
		"""
		cdef int ret
		if xmmsv_get_int(self.val, &ret):
			return ret
		else:
			raise ValueError("Failed to retrieve value!")

	def get_string(self):
		"""
		Get data from the result structure as a string.
		@rtype: string
		"""
		cdef char *ret

		if xmmsv_get_string(self.val, <xmms_pyrex_constcharpp_t>&ret):
			return to_unicode(ret)
		else:
			raise ValueError("Failed to retrieve value!")

	def get_bin(self):
		"""
		Get data from the result structure as binary data.
		@rtype: string
		"""
		cdef unsigned char *ret
		cdef unsigned int rlen

		if xmmsv_get_bin(self.val, <xmms_pyrex_constucharpp_t> &ret, &rlen):
			return PyString_FromStringAndSize(<char *>ret, rlen)
		else:
			raise ValueError("Failed to retrieve value!")

	def get_dict(self) :
		"""
		@return: A dictionary containing media info.
		"""
		cdef xmmsv_dict_iter_t *it
		cdef xmms_pyrex_constcharp_t k
		cdef xmmsv_t *v
		cdef XMMSValue V
		ret = {}

		if not xmmsv_get_dict_iter(self.val, &it):
			raise RuntimeError("Failed to get dict iterator")

		while xmmsv_dict_iter_valid(it):
			xmmsv_dict_iter_pair(it, &k, &v)
			V=XMMSValue(self.sourcepref)
			V.set_value(v)

			ret[<char *>k] = V.value()
			xmmsv_dict_iter_next(it)
		xmmsv_dict_iter_explicit_destroy (it)
		return ret

	def get_propdict(self):
		"""
		@return: A source dict.
		"""
		ret = PropDict(self.sourcepref)
		for key, values in self.get_dict().iteritems():
			for source, value in values.iteritems():
				ret[(source,key)] = value
		return ret

	def get_list (self) :
		"""
		@return: A list of dicts from the result structure.
		"""
		cdef xmmsv_list_iter_t *iter
		cdef xmmsv_t *val
		cdef XMMSValue obj

		ret = []

		xmmsv_get_list_iter(self.val, &iter)
		while xmmsv_list_iter_valid(iter):
			xmmsv_list_iter_entry(iter, &val)

			obj = XMMSValue(self.sourcepref)
			obj.set_value(val)

			ret.append(obj.value())

			xmmsv_list_iter_next(iter)
		xmmsv_list_iter_explicit_destroy(iter)
		return ret

	def iserror(self):
		"""
		@deprecated
		@return: Whether the value represents an error or not.
		@rtype: Boolean
		"""

		return self.is_error()

	def is_error(self):
		"""
		@return: Whether the value represents an error or not.
		@rtype: Boolean
		"""
		return xmmsv_is_error(self.val)

	def get_error(self):
		"""
		@return: Error string from the result.
		@rtype: String
		"""
		cdef char *ret

		if xmmsv_get_error(self.val, <xmms_pyrex_constcharpp_t>&ret):
			return to_unicode(ret)
		else:
			raise ValueError("Failed to retrieve value!")

	def __dealloc__(self):
		if self.val != NULL:
			xmmsv_unref(self.val)
		self.val = NULL

cdef class XMMSResult:
	"""
	Class containing the results of some operation
	"""
	cdef xmmsc_result_t *res
	cdef object callback
	cdef object exc
	cdef object xmms
	cdef int ispropdict

	def __init__(self):
		self.callback = None
		self.ispropdict = 0

	cdef set_connection(self, conn):
		self.xmms = conn

	cdef set_result(self, xmmsc_result_t *res):
		self.res = res

	cdef xmmsc_result_t* get_result(self):
		return self.res

	cdef set_callback(self, callback):
		assert(callback)

		self.callback = callback

		Py_INCREF(self)

		xmmsc_result_notifier_set_full(self.res, ResultNotifier, <void *> self, ObjectFreeer)

	def _callback(self):
		try:
			ret = self.callback(self._value())
		except:
			exc = sys.exc_info()
			traceback.print_exception (exc[0], exc[1], exc[2])
			return 0

		# None most likely means missing returvalue,
		# which most likely means user wants restart
		if ret is None:
			ret = 1

		return ret

	def wait(self):
		"""
		Wait for the result from the daemon.
		"""
		xmmsc_result_wait(self.res)
		if self.exc is not None:
			raise self.exc[0], self.exc[1], self.exc[2]

	def iserror(self):
		"""
		@return: Whether the result represents an error or not.
		@rtype: Boolean
		"""
		return self._value().iserror()

	def _value(self):
		cdef XMMSValue obj
		cdef xmmsv_t *value

		value = xmmsc_result_get_value(self.res)

		obj = XMMSValue(self.xmms.get_source_preference())
		obj.set_value(value)
		obj.ispropdict = self.ispropdict

		return obj

	def value(self):
		return self._value().value()

	def __dealloc__(self):
		"""
		Deallocate the result.
		"""
		if self.res != NULL:
			xmmsc_result_unref(self.res)
		self.res = NULL

cdef void python_need_out_fun(int i, void *obj):
	cdef object o
	o = <object> obj
	o._needout_cb(i)

cdef void python_disconnect_fun(void *obj):
	cdef object o
	o = <object> obj
	o._disconnect_cb()

cdef class XMMS:
	"""
	This is the class representing the XMMS2 client itself. The methods in
	this class may be used to control and interact with XMMS2.
	"""
	cdef xmmsc_connection_t *conn
	cdef object do_loop
	cdef object wakeup
	cdef object disconnect_fun
	cdef object needout_fun
	cdef object sources

	def __cinit__(self, clientname = None):
		"""
		Initiates a connection to the XMMS2 daemon. All operations
		involving the daemon are done via this connection.
		"""
		if clientname is None:
			clientname = "UnnamedPythonClient"

		tmp = from_unicode(clientname)

		self.conn = xmmsc_init(tmp)
		if self.conn == NULL:
			raise ValueError("Failed to initialize xmmsclient library! Probably due to broken name.")

		self.sources = ["client/" + clientname, "server", "plugins/*", "client/*", "*"]

	def get_source_preference(self):
		return self.sources
	def set_source_preference(self, sources):
		self.sources = sources

	def __dealloc__(self):
		""" destroys it all! """
		if self.conn != NULL:
			xmmsc_unref(self.conn)
		self.conn = NULL

	def _needout_cb(self, i):
		if self.needout_fun is not None:
			self.needout_fun(i)

	def _disconnect_cb(self):
		if self.disconnect_fun is not None:
			self.disconnect_fun(self)

	def exit_loop(self):
		"""
		exit_loop()

		Exits from the L{loop} call
		"""
		self.do_loop = False
		write(self.wakeup, "42")

	def loop(self):
		"""
		loop()

		Main client loop for most python clients. Call this to run the
		client once everything has been set up. This function blocks
		until L{exit_loop} is called.
		"""
		fd = xmmsc_io_fd_get(self.conn)
		(r, w) = os.pipe()

		self.do_loop = True
		self.wakeup = w

		while self.do_loop:

			if self.want_ioout():
				w = [fd]
			else:
				w = []

			(i, o, e) = select([fd, r], w, [fd])

			if i and i[0] == fd:
				xmmsc_io_in_handle(self.conn)
			if o and o[0] == fd:
				xmmsc_io_out_handle(self.conn)
			if e and e[0] == fd:
				xmmsc_io_disconnect(self.conn)
				self.do_loop = False

	def ioin(self):
		"""
		ioin() -> bool

		Read data from the daemon, when available. Note: This is a low
		level function that should only be used in certain
		circumstances. e.g. a custom event loop
		"""
		return xmmsc_io_in_handle(self.conn)

	def ioout(self):
		"""
		ioout() -> bool

		Write data out to the daemon, when available. Note: This is a
		low level function that should only be used in certain
		circumstances. e.g. a custom event loop
		"""
		return xmmsc_io_out_handle(self.conn)

	def want_ioout(self):
		"""
		want_ioout() -> bool
		"""
		return xmmsc_io_want_out(self.conn)

	def set_need_out_fun(self, fun):
		"""
		set_need_out_fun(fun)
		"""
		Py_INCREF(self)
		xmmsc_io_need_out_callback_set_full(self.conn, python_need_out_fun, <void *>self, ObjectFreeer)
		self.needout_fun = fun
		
	def get_fd(self):
		"""
		get_fd() -> int

		Get the underlying file descriptor used for communication with
		the XMMS2 daemon. You can use this in a client to ensure that
		the IPC link is still active and safe to use.(e.g by calling
		select() or poll())
		@rtype: int
		@return: IPC file descriptor
		"""
		return xmmsc_io_fd_get(self.conn)

	def connect(self, path = None, disconnect_func = None):
		"""
		connect(path=None, disconnect_func=None)

		Connect to the appropriate IPC path, for communication with the
		XMMS2 daemon. This path defaults to /tmp/xmms-ipc-<username> if
		not specified. Call this once you have instantiated the object:

		C{import xmmsclient}

		C{xmms = xmmsclient.XMMS()}

		C{xmms.connect()}

		...
		
		You can provide a disconnect callback function to be activated
		when the daemon disconnects.(e.g. daemon quit) This function
		typically has to exit the main loop used by your application.
		For example, if using L{loop}, your callback should call
		L{exit_loop} at some point.
		"""
		if path:
			ret = xmmsc_connect(self.conn, path)
		else:
			ret = xmmsc_connect(self.conn, NULL)

		if not ret:
			raise IOError("Couldn't connect to server!")

		self.disconnect_fun = disconnect_func
		Py_INCREF(self)
		xmmsc_disconnect_callback_set_full(self.conn, python_disconnect_fun, <void *>self, ObjectFreeer)


	def quit(self, cb = None):
		"""
		quit(cb=None) -> XMMSResult

		Tell the XMMS2 daemon to quit.
		@rtype: L{XMMSResult}
		@return: The result of the operation.
		"""
		return self.create_result(cb, xmmsc_quit(self.conn))

	cdef create_result(self, cb, xmmsc_result_t *res):
		cdef XMMSResult ret

		if res == NULL:
			raise RuntimeError("xmmsc_result_t couldn't be allocated")

		ret = XMMSResult()
		ret.set_result(res)
		ret.set_connection(self)
		if cb is not None:
			ret.set_callback(cb)

		return ret

	def visualization_version(self, cb=None):
		"""
		visualization_version(cb=None) -> XMMSResult

		Get the visualization version from the server
		@rtype: L{XMMSResult}
		@return: The result of the operation.
		"""
		return self.create_result(cb, xmmsc_visualization_version(self.conn))

	def visualization_init(self, cb=None):
		"""
		visualization_init(cb) -> XMMSResult

		Initialize visualization effect.
		@rtype: L{XMMSResult}
		@return: The result of the operation.
		"""
		return self.create_result(cb, xmmsc_visualization_init(self.conn))

	def visualization_init_handle(self, XMMSResult result):
		"""
		visualization_init_handle(result) -> Int
		
		Get a handle to visualization.
		On failure return value is < 0.
		@rtype: C{Int}
		@return: The result of the operation.
		"""
		return xmmsc_visualization_init_handle(result.get_result())

	def visualization_start(self, int vv, cb = None):
		"""
		visualization_start(vv, cb) -> XMMSResult

		Start the visualization.
		@rtype: L{XMMSResult}
		@return: The result of the operation
		"""
		return self.create_result(cb, xmmsc_visualization_start(self.conn, vv))

	def visualization_start_handle(self, XMMSResult result):
		"""
		visualization_start_handle(result) -> None

		Handle the start of the visualization.
		Should be called when the visualization started
		"""
		xmmsc_visualization_start_handle(self.conn, result.get_result())

	def visualization_started(self, int vv):
		"""
		visualization_started(vv) -> Boolean

		Check if visualization was started
		@rtype: {Boolean}
		@return: The result of the operation
		"""
		return 1 == xmmsc_visualization_started(self.conn, vv)

	def visualization_errored(self, int vv):
		"""
		visualization_errored(vv) -> Boolean

		Check if visualization errored
		@rtype: {Boolean}
		@return: The result of the operation
		"""
		return xmmsc_visualization_errored(self.conn, vv)

	def get_last_error(self):
		"""
		get_last_error(self) -> String

		Get the last error.
		@rtype: {String}
		@return: The result of the operation.
		"""
		cdef char* errmsg
		errmsg = xmmsc_get_last_error(self.conn)
		if errmsg:
			return to_unicode(errmsg)
		return to_unicode("")


	def visualization_property_set(self, int vv, key, value, cb=None):
		"""
		visualization_property_set(vv, key, value, cb=None) -> XMMSResult

		Set visualization property.
		@rtype: L{XMMSResult}
		@return: The result of the operation.
		"""
		c1 = from_unicode(key)
		c2 = from_unicode(val)
		return self.create_result(cb, xmmsc_visualization_property_set(self.conn, vv, c1, c2))

	def visualization_properties_set(self, int vv, prop_dict, cb=None):
		"""
		visualization_properties_set(vv, prop_dict, cb=None) -> XMMSResult

		Set properties for visualization id 'vv' described by
		dictionary prop_dict
		@rtype: L{XMMSResult}
		@return: The result of the operation.
		"""
		return self.create_result(cb, xmmsc_visualization_properties_set(self.conn, vv, create_native_value(prop_dict)))

	def visualization_chunk_get(self, int vv, int drawtime, unsigned int blocking, unsigned int buffer_size = 1024):
		"""
		visualization_chunk_get(vv, drawtime, blocking) -> list

		Returns a list of ints as a result.
		@rtype: L{tuple}
		@return: The result of the operation.
		
		TODO access XMMS_VISUALIZATION_WINDOW_SIZE, to determine
		max buffer length
		"""
		cdef short *buffer
		cdef int bint, i
		cdef list rlist
		buffer = <short *>PyMem_Malloc(buffer_size*sizeof(short))
		if buffer == NULL:
			return None
		bint = xmmsc_visualization_chunk_get( self.conn, vv, buffer, drawtime, blocking)
		rlist = []
		for i from 0 <= i < bint:
			rlist.append(buffer[i])
		PyMem_Free(buffer)
		return rlist
	
	def visualization_shutdown(self, int vv):
		"""
		visualization_shutdown(self, vv) -> None

		Stop the visualization.
		"""
		xmmsc_visualization_shutdown(self.conn, vv)


